package main

import (
	"context"
	"fmt"
	"go.etcd.io/etcd/clientv3"
	"time"
)

// 执行2个 crontab.go
func main() {
	var(
		config clientv3.Config  // 客户端配置
		client *clientv3.Client // 客户端
		err error
		kv clientv3.KV          // kv客户端
		lease          clientv3.Lease    // 租约服务
		leaseGrantResp *clientv3.LeaseGrantResponse
		leaseid        clientv3.LeaseID
		KeepResp       *clientv3.LeaseKeepAliveResponse        // 续租通道
		KeepRespChan   <-chan *clientv3.LeaseKeepAliveResponse // 续租通道
		ctx context.Context
		cancelFunc context.CancelFunc
		txn clientv3.Txn // 事务相关
		txnResp *clientv3.TxnResponse
	)
	// 配置
	config = clientv3.Config{
		Endpoints:[]string{"127.0.0.1:2379"},
		DialTimeout:5*time.Second,
	}

	// 连接客户端
	if client,err = clientv3.New(config);err != nil{
		fmt.Println(err)
		return
	}

	kv = clientv3.NewKV(client)

	// 上锁，处理业务，解锁

	// 1 上锁(创建租约，自动续租，抢占key)
	// 申请租约
	lease = clientv3.NewLease(client)

	// 设置租约为5秒
	if leaseGrantResp, err = lease.Grant(context.TODO(), 5); err != nil {
		fmt.Println(err)
	}

	// 租约id
	leaseid = leaseGrantResp.ID

	// 取消自动续租的上下文
	ctx,cancelFunc = context.WithCancel(context.TODO())

	// 自动执行,取消续租，在最后 time.Sleep(5*time.Second) 后调用
	defer cancelFunc()
	defer lease.Revoke(context.TODO(),leaseid)

	// 每次续租时间为申请的时间
	if KeepRespChan, err = lease.KeepAlive(ctx, leaseid); err != nil {
		fmt.Println(err)
	}

	//
	go func() {
		for {
			select {
			case KeepResp = <-KeepRespChan:
				// 特殊情况，手动让租约失效
				if KeepRespChan == nil {
					fmt.Println("租约失效了")
					goto END
				} else {
					fmt.Println("收到续租应答", KeepResp.ID)
				}
			}
		}
	END:
	}()

	// 创建事务
	txn = kv.Txn(context.TODO())

	// 事务处理
	txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/lock/job9"),"=",0)).
		Then(clientv3.OpPut("/cron/lock/job9","this is job9",clientv3.WithLease(leaseid))).
		Else(clientv3.OpGet("/cron/lock/job9"))

	// 提交事务
	if txnResp,err = txn.Commit();err !=nil{
		fmt.Println(err)
		return
	}

	if !txnResp.Succeeded{
		fmt.Println("锁被占用",string(txnResp.Responses[0].GetResponseRange().Kvs[0].Value))
		return
	}


	// 2 处理业务
	fmt.Println("这是任务")
	time.Sleep(5*time.Second)


	// 3. 释放锁(取消租约、释放租约) 。参见 defer

}
